Category: Reverse Engineering
Difficulty: Easy
Author: 0x4d5a
.NET Reversing can't be that hard, right? But I've got some twists waiting for you 😈
Execute with .NET Core Runtime 2.2 with windows, e.g. dotnet ReMe.dll
The author provided some files and a .NET
dll.
When we run reme
without any arguments, it shows the usage:
> dotnet ReMe.dll
Usage: ReMe.exe [password] [flag]
If we run reme
with arguments it says Nope
:
> dotnet ReMe.dll AAA BBB
Nope
Like for Java, the Decompilers for .NET are quite good therefore I fired up dnSpy
and opened the dll.
using System; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; namespace ReMe { // Token: 0x02000003 RID: 3 internal class Program { // Token: 0x06000005 RID: 5 RVA: 0x00002075 File Offset: 0x00000275 static Program() { Program.Initialize(); } // Token: 0x06000006 RID: 6 RVA: 0x00002178 File Offset: 0x00000378 private static void Main(string[] args) { Program.InitialCheck(args); [...]
In Main
the function Program.InitialCheck(args);
is called with the program arguments. In this function is the first Flag:
// ReMe.Program // Token: 0x0600000C RID: 12 RVA: 0x000022BC File Offset: 0x000004BC private static void InitialCheck(string[] args) { Program.Initialize(); bool isAttached = Debugger.IsAttached; if (isAttached) { Console.WriteLine("Nope"); Environment.Exit(-1); } bool flag = true; Program.CheckRemoteDebuggerPresent(Process.GetCurrentProcess().Handle, ref flag); bool flag2 = flag; if (flag2) { Console.WriteLine("Nope"); Environment.Exit(-1); } bool flag3 = Program.IsDebuggerPresent(); if (flag3) { Console.WriteLine("Nope"); Environment.Exit(-1); } bool flag4 = args.Length == 0; if (flag4) { Console.WriteLine("Usage: ReMe.exe [password] [flag]"); Environment.Exit(-1); } bool flag5 = args[0] != StringEncryption.Decrypt("D/T9XRgUcKDjgXEldEzeEsVjIcqUTl7047pPaw7DZ9I="); if (flag5) { Console.WriteLine("Nope"); Environment.Exit(-1); } else { Console.WriteLine("There you go. Thats the first of the two flags! CSCG{{{0}}}", args[0]); } IntPtr moduleHandle = Program.GetModuleHandle("kernel32.dll"); bool flag6 = moduleHandle != IntPtr.Zero; if (flag6) { IntPtr procAddress = Program.GetProcAddress(moduleHandle, "CheckRemoteDebuggerPresent"); bool flag7 = Marshal.ReadByte(procAddress) == 233; if (flag7) { Console.WriteLine("Nope!"); Environment.Exit(-1); } } }
But the flag is encrypted, we could Reverse-Engineer the StringEncryption.Decrypt
function or just set a breakpoint at at the return
inside the Decrypt
function and watch the locals
, because the flag is decrypted in memory after that call. Because of some bug we have to also set a breakpoint in .cctor()
otherwise we would not be able to see the locals.
Now just press continue until we hit the breakpoint.
And the flag is in the locals after cipherText
, just wrap it in the flag format CSCG{<cipherText>}
.
CSCG{CanIHazFlag?}